package com.box.app.arithmetic

import java.lang.Math
import java.math.BigDecimal
import kotlin.reflect.full.functions
import kotlin.reflect.full.staticFunctions

@Suppress("EnumEntryName")
enum class BuiltInMath : Callable {
    // region ================ Math ========================================
    abs,
    sin,
    cos,
    tan,
    atan2,
    sqrt,
    log,
    log10,
    pow,
    exp,
    min,
    max,
    floor,
    ceil,
    rint,
    addExact,
    decrementExact,
    incrementExact,
    multiplyExact,
    multiplyHigh,
    negateExact,
    subtractExact,
    fma,
    scalb,
    copySign,
    getExponent,
    signum,
    floorMod,
    asin,
    acos,
    atan,
    cbrt,
    IEEEremainder,
    floorDiv,
    powerOfTwoD,
    powerOfTwoF,
    sinh,
    cosh,
    tanh,
    hypot,
    expm1,
    log1p,
    toRadians,
    toDegrees,
    round,
    random,
    toIntExact,
    multiplyFull,
    ulp,
    nextAfter,
    nextUp,
    nextDown,
    // endregion
    ;

    override fun call(vararg values: Number): Number {
        var function = BigDecimal::class.functions.firstOrNull { it.name == name }
        if (function == null) {
            function = Math::class.staticFunctions.first { it.name == name }
        }
        val arity = function.parameters.size
        if (arity != values.size) {
            throw IllegalArgumentException("$name expects $arity arguments, but ${values.size} were provided.")
        }
        val args = function.parameters.map { values[it.index].to(it.type) }
        return function.call(*(args.toTypedArray())) as Number
    }
}
